package utils

import (
	"bytes"
	"fmt"
	"github.com/spf13/viper"
	"io"
	"os"
	"path"
	"strings"
	"time"

	"crypto/tls"
	"github.com/pkg/errors"
	"net/http"
	"net/url"
)

type UrlHelper struct {
}

func NewUrlHelper() *UrlHelper {
	return new(UrlHelper)
}

func (u *UrlHelper) ChangeHost(originalStr, newHost string) string {
	originalPtr, _ := url.Parse(originalStr)
	if newHost != "" {
		originalPtr.Host = newHost
		if strings.Contains(newHost, "//") {
			originalPtr.Host = strings.Split(newHost, "//")[1]
			originalPtr.Scheme = strings.TrimSuffix(strings.Split(newHost, "//")[0], ":")
		}
	}
	return originalPtr.String()
}

// Get
func Get(apiUrl string, parm map[string]string, header map[string]string, timeout ...int) ([]byte, error) {
	if len(parm) > 0 {
		apiUrl = fmt.Sprintf("%s%s", apiUrl, "?")
		p := ""
		for k, v := range parm {
			if p == "" {
				p = fmt.Sprintf("%s=%s", k, v)
			} else {
				p = fmt.Sprintf("%s&%s=%s", p, k, v)
			}
		}
		apiUrl = fmt.Sprintf("%s%s", apiUrl, p)
	}

	var client *http.Client
	if len(timeout) > 0 {
		client = &http.Client{Timeout: time.Duration(timeout[0]) * time.Second}
	} else {
		client = &http.Client{}
	}

	if len(apiUrl) > 5 && apiUrl[0:5] == "https" {
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}
	reqest, err := http.NewRequest("GET", apiUrl, nil)
	if err != nil {
		return nil, err
	}
	for k, v := range header {
		reqest.Header.Set(k, v)
	}
	response, err := client.Do(reqest)
	if nil != err {
		return nil, err
	}

	defer response.Body.Close()
	if response.StatusCode != 200 {
		return nil, errors.New(response.Status)
	}

	body, err := io.ReadAll(response.Body)
	if nil != err {
		return nil, err
	}

	return body, nil
}

func JsonFile(apiUrl, key string, data []byte) error {

	file := ""
	switch apiUrl {
	case viper.GetString("ext_api.cover"):
		file = "cover"
	case viper.GetString("ext_api.driven"):
		file = "driven"
	case viper.GetString("ext_api.html_to_png"):
		file = "driven"
	case viper.GetString("ext_api.merge"):
		file = "merge"
	}
	if file != "" {
		savePath := path.Join("./resources/"+file, key+".json")
		os.WriteFile(savePath, data, 0644)
	}
	return nil
}

// post
func Post(apiUrl string, data []byte, header map[string]string, timeout ...int) (code int, body []byte, err error) {

	timeout = append(timeout, 0)
	client := &http.Client{Timeout: time.Duration(timeout[0]) * time.Second}

	if len(apiUrl) > 5 && apiUrl[0:5] == "https" {
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}
	reqest, _ := http.NewRequest("POST", apiUrl, bytes.NewReader(data))

	if header == nil {
		header = map[string]string{}
	}

	if _, ok := header["Content-Type"]; !ok {
		header["Content-Type"] = "application/x-www-form-urlencoded"
	}

	for k, v := range header {
		reqest.Header.Set(k, v)
	}

	response, err := client.Do(reqest)
	if nil != err {
		return
	}

	defer response.Body.Close()

	code = response.StatusCode
	body, err = io.ReadAll(response.Body)
	if nil != err {
		return
	}

	if response.StatusCode != 200 {
		err = errors.New(response.Status)
		return
	}

	return
}

// post
func PostMap(apiUrl string, parm map[string]string, header map[string]string, isHttps bool) ([]byte, error) {

	data := url.Values{}
	for k, v := range parm {
		data.Set(k, v)
	}

	reqParams := io.NopCloser(strings.NewReader(data.Encode()))
	client := &http.Client{}

	if isHttps {
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}
	reqest, _ := http.NewRequest("POST", apiUrl, reqParams)

	for k, v := range header {
		reqest.Header.Set(k, v)
	}

	response, err := client.Do(reqest)
	if nil != err {
		return nil, err
	}

	defer response.Body.Close()
	if response.StatusCode != 200 {
		return nil, errors.New(response.Status)
	}

	body, err := io.ReadAll(response.Body)
	if nil != err {
		return nil, err
	}

	return body, nil
}

// 获取远程ip
func GetRemoteIP(r *http.Request) string {
	addr := r.Header.Get("Remote_addr")
	if addr == "" {
		addr = r.RemoteAddr
	}

	return strings.Split(addr, ":")[0]
}

// ParseQuery 参数解析，兼容部分字段有encode部分没encode的情况
func ParseQuery(query string) (m url.Values, err error) {
	m = make(url.Values)
	for query != "" {
		key := query
		if i := strings.IndexAny(key, "&"); i >= 0 {
			key, query = key[:i], key[i+1:]
		} else {
			query = ""
		}
		if key == "" {
			continue
		}
		value := ""
		if i := strings.Index(key, "="); i >= 0 {
			key, value = key[:i], key[i+1:]
		}
		key, err1 := url.QueryUnescape(key)
		if err1 != nil {
			if err == nil {
				err = err1
			}
			continue
		}
		value1, err1 := url.QueryUnescape(value)
		if err1 != nil {
			if !strings.HasPrefix(err1.Error(), "invalid URL escape") {
				if err == nil {
					err = err1
				}
				continue
			}
			m[key] = append(m[key], value)
		} else {
			m[key] = append(m[key], value1)
		}
	}
	return m, err
}

// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func SetCookie(w http.ResponseWriter, name, value string, maxAge int, path, domain string, secure, httpOnly bool, sameSite http.SameSite) {
	if path == "" {
		path = "/"
	}
	http.SetCookie(w, &http.Cookie{
		Name:     name,
		Path:     path,
		MaxAge:   maxAge,
		Domain:   domain,
		Secure:   secure,
		SameSite: sameSite,
		HttpOnly: httpOnly,
		Value:    url.QueryEscape(value),
	})
}

func SetCookie2(w http.ResponseWriter, cookie *http.Cookie) {
	if cookie.Path == "" {
		cookie.Path = "/"
	}
	http.SetCookie(w, cookie)
}

// GetCookie returns the named cookie provided in the request or
// ErrNoCookie if not found. And return the named cookie is unescaped.
// If multiple cookies match the given name, only one cookie will
// be returned.
func GetCookie(reqest *http.Request, name string) (string, error) {
	cookie, err := reqest.Cookie(name)
	if err != nil {
		return "", err
	}
	val, _ := url.QueryUnescape(cookie.Value)
	return val, nil
}

// HTTPRequestData 请求返回的数据结构
type HTTPRequestData struct {
	HTTPCode int
	Data     string
}

/*
HTTPRequest http请求
*/
func HTTPRequest(method, urls string, body io.Reader) (*HTTPRequestData, error) {

	// caCrt, err := strings.NewReader(data)
	var reqest *http.Request
	var err error
	client := &http.Client{}
	if method == "POST" {
		reqest, err = http.NewRequest(method, urls, body)
	} else {
		reqest, err = http.NewRequest(method, urls, body)
	}

	if method == "POST" {
		// reqest.Header.Set("Content-Type", "multipart/form-data;boundary=8fc18f47c62b1fcdb4e9a4bd4c6d9ec48d72794c45d6ab2877cb63f30596")
		reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
	}
	if err != nil {
		return nil, err
	}
	response, err := client.Do(reqest)

	if err != nil {
		return nil, err
	}
	status := response.StatusCode
	byt, err := io.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	return &HTTPRequestData{
		HTTPCode: status,
		Data:     string(byt),
	}, nil
}

/*
HTTPSetHeaderRequest 头设置请求
*/
func HTTPSetHeaderRequest(method string, heard map[string]string, urls string, body io.Reader) (*HTTPRequestData, error) {
	var reqest *http.Request
	var err error
	client := &http.Client{}
	reqest, err = http.NewRequest(method, urls, body)
	for keys, val := range heard {
		reqest.Header.Set(keys, val)
	}
	if err != nil {
		return nil, err
	}

	response, err := client.Do(reqest)
	if err != nil {
		return nil, err
	}

	status := response.StatusCode
	byt, err := io.ReadAll(response.Body)

	response.Body.Close()
	if err != nil {
		return nil, err
	}

	return &HTTPRequestData{
		HTTPCode: status,
		Data:     string(byt),
	}, err

}

/*
HTTPPOSTRequest post
*/
func HTTPPOSTRequest(urls string, data url.Values) (*HTTPRequestData, error) {

	if data == nil {
		return HTTPRequest("POST", urls, nil)
	}

	return HTTPRequest("POST", urls, io.NopCloser(strings.NewReader(data.Encode())))
}

/*
HTTPGetRequest get
*/
func HTTPGetRequest(url string, data interface{}) (*HTTPRequestData, error) {
	return HTTPRequest("GET", url, nil)
}

func GenerateQueryStr(keyValue map[string]interface{}) string {
	str := ""
	for key, value := range keyValue {
		oneKeyValueStr := key + "=" + fmt.Sprintf("%s", value)
		switch str {
		case "":
			str = "?" + oneKeyValueStr
		default:
			str += "&" + oneKeyValueStr
		}
	}
	return str
}
